import math
import re
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from datetime import datetime, timedelta
import json
import os
import smtplib
import threading
from docx import Document
from docx.shared import RGBColor, Pt
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT, WD_COLOR_INDEX  # 新增WD_COLOR_INDEX导入
from docx.enum.style import WD_STYLE_TYPE
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
import tkinter.messagebox  # 新增导入
from ConfigManager import ConfigManager
from DocxStyleHandler import DocxStyleHandler
from JsonUtil import JsonUtil
from LLMProcess import LLMProcess
from PromptGenerator import PromptGenerator
from CustomDialog import CustomDialog
import Logger_config
from ui.ProgressBar import ProgressOverlay 
from TimePredictor import TimePredictor

# 设置日志配置
logger = Logger_config.setup_logger()

class WordChecker:
    def __init__(self,root): 
        self.root = root       
        self.cm = ConfigManager()
    
    def check(self, config: dict):
        # 入参校验
        if not self.validate_config(config):
            return
        # 颜色校验
        if self.check_color(config):  
            return
        
        #大模型参数获取
        model_base_url = self.cm.get_merged_config()['model']['base_url']  # 模型的基础URL
        model_api_key = self.cm.get_merged_config()['model']['api_key']  # 模型的API密钥
        model_name = self.cm.get_merged_config()['model']['model_name']  # 模型名称
        model_max_tokens = self.cm.get_merged_config()['model']['max_tokens']  # 设置模型生成的最大token数，控制生成内容的长度
        model_temperature = self.cm.get_merged_config()['model']['temperature']  # 设置模型的温度参数，控制生成内容的随机性，值越高生成的内容越随机
        model_stream = self.cm.get_merged_config()['model']['stream']  # 设置是否以流式方式获取模型生成的内容，True表示逐步返回生成结果，False表示一次性返回.

        # 获取系统提示和用户提示
        system_prompt,user_prompt = PromptGenerator.generate_prompt("check", config)        
        prompts = [
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": user_prompt}
                ]
        logger.info(f"文档检查系统提示词长度：{len(system_prompt)}，内容：{system_prompt}")
        logger.info(f"文档检查用户提示词长度：{len(user_prompt)}，内容：{user_prompt}")

        tp = TimePredictor()
        times = tp.predict_time(len(user_prompt)+len(system_prompt), "check")       

         # 延迟到主线程创建进度条
        self.root.after(0, self._show_progress_bar, times)         
        
        # 在新线程中执行模型调用
        threading.Thread(
            target=self._run_model_call_in_thread,
            args=(model_name, model_api_key,model_base_url, model_max_tokens, model_temperature,model_stream, prompts),
            daemon=True
        ).start()
                    
    # 入参校验
    def validate_config(self, config: dict) -> bool:
        """
        入参校验
        
        参数:
            config (dict): 入参
            
        返回:
            bool: 入参是否有效            
        """
        # 启用双文件比较的情况
        if config.get('compare_files_check'):
            if not config.get('file_path_compare1') or not config.get('file_path_compare2'):
                tkinter.messagebox.showwarning("警告", "【启用双文件比较】时，请选择【基准文件】和【新版文件】")
                return False
            if config.get('file_path_compare1') == config.get('file_path_compare2'):                
                tkinter.messagebox.showwarning("警告", "【基准文件】和【新版文件】不能相同")
                return False
                
            if not config.get('output_similarity_ratio_check') and not config.get('output_similarity_content_check'):
                tkinter.messagebox.showwarning("警告", "【启用双文件比较】时，【输出相似度比例】和【输出相似度内容】两个复选框至少选择一个")
                return False                
        
        # 未启用双文件比较的情况
        if not config.get('compare_files_check'):
            if not config.get('file_path_compare1'):
                tkinter.messagebox.showwarning("警告", "请选择Word文档")
                return False
            
            # 检查三个版块是否至少有一个检查项被选中
            text_accuracy = any([
                config.get('spell_check'),
                config.get('punctuation_check'),
                config.get('grammar_check'),
                config.get('redundancy_check'),
                config.get('terminology_check')
            ])
            
            data_compliance = any([
                config.get('link_check'),
                config.get('date_check')
            ])
            
            formatting = any([
                config.get('font_color_check'),
                config.get('font_bgcolor_check')
            ])
            
            if not (text_accuracy or data_compliance or formatting):
                tkinter.messagebox.showwarning("警告", "文本准确性、数据与合规、格式与标识三个版块中至少选择一个检查项")
                return False
            
        # 日期范围检查必须输入开始日期和结束日期
        if config.get('date_check'):
            # 如果开始日期或结束日期为空，则提示
            if not config.get('start_date') or not config.get('end_date'):
                tkinter.messagebox.showwarning("警告", "日期范围检查已启用，必须输入开始日期和结束日期")
                return False
            
            # 结束日期必须在开始日期之后
            try:
                start_date = datetime.strptime(config['start_date'], '%Y-%m-%d')
                end_date = datetime.strptime(config['end_date'], '%Y-%m-%d')
                if end_date < start_date:
                    tkinter.messagebox.showwarning("警告", "结束日期必须晚于开始日期")
                    return False
            except ValueError:
                tkinter.messagebox.showwarning("警告", "日期格式无效，请使用YYYY-MM-DD格式")
                return False
                
        return True
    
    def check_color(self, config: dict) -> bool:
        if config.get('compare_files_check'):                
            file_path = config.get('file_path_compare2')
        else:
            file_path = config.get('file_path_compare1')   

        # 字体前景色检查
        if config.get('font_color_check'):                   
            (exist, target_color_texts), _ = self.check_text_color(file_path, target_fg_color_hex=config.get('specified_color'))
            if exist:
                target_color_texts = "文档中存在指定前景色字体,详情如下：\n\n" + target_color_texts               
                CustomDialog(self.root, "文档字体前景色检查结果", target_color_texts, type='info')
                return True
            else:
                return False
        
        # 字体背景色检查
        if config.get('font_bgcolor_check'):
            # 使用新的检查方法，只检查背景色
            _, (bg_exist, bg_texts) = self.check_text_color(file_path,target_bg_color_hex=config.get('specified_bgcolor'))
            if bg_exist:    
                bg_texts = "文档中存在指定背景色字体,详情如下：\n\n" + bg_texts     
                CustomDialog(self.root, "文档字体背景色检查结果", bg_texts, type='info')
                return True
            else:
                return False
    def check_text_color(self, file_path, target_fg_color_hex=None, target_bg_color_hex=None):
        """
        检查文档中是否存在指定前景色或背景色的文本，支持同时检查两种颜色
        
        参数:
        - file_path: str，要检查的Word文档的文件路径
        - target_fg_color_hex: str，目标前景色的十六进制值（如"#FF0000"），可选
        - target_bg_color_hex: str，目标背景色的十六进制值（如"#FFFFFF"），可选
        
        返回:
        - tuple: (fg_result, bg_result)
          - fg_result: (bool, str) 前景色检查结果
            - 第一个元素表示是否存在指定前景色的文本
            - 第二个元素是所有匹配文本的字符串，每个文本用换行符分隔
          - bg_result: (bool, str) 背景色检查结果
            - 第一个元素表示是否存在指定背景色的文本
            - 第二个元素是所有匹配文本的字符串，每个文本用换行符分隔
        """
        doc = Document(file_path)
        fg_exist = False
        bg_exist = False
        fg_texts = []
        bg_texts = []

        # 遍历所有段落
        for para in doc.paragraphs:
            for run in para.runs:
                if not run.text.strip():  # 跳过空文本
                    continue
                
                # 检查前景色（如果指定了目标前景色）
                if target_fg_color_hex:
                    font_color = run.font.color.rgb
                    if font_color:  # 确保有颜色设置
                        hex_color = f"#{font_color[0]:02X}{font_color[1]:02X}{font_color[2]:02X}"
                        if self._is_color_similar(hex_color, target_fg_color_hex):
                            logger.info(f"发现指定前景色文本: '{run.text}'，颜色值: {hex_color}")
                            fg_exist = True
                            fg_texts.append(run.text)
                
                # 检查背景色（如果指定了目标背景色）
                if target_bg_color_hex:
                    highlight_color = run.font.highlight_color
                    if highlight_color:
                        if highlight_color != WD_COLOR_INDEX.NO_HIGHLIGHT:
                            highlight_rgb = self._highlight_color_to_rgb(highlight_color)                            
                            if highlight_rgb:
                                hex_color = f"#{highlight_rgb[0]:02X}{highlight_rgb[1]:02X}{highlight_rgb[2]:02X}"
                                if self._is_color_similar(hex_color, target_bg_color_hex):
                                    logger.info(f"发现指定背景色文本: '{run.text}'，背景色: {hex_color}")
                                    bg_exist = True
                                    bg_texts.append(run.text)
                                    
                    #检查字符底纹背景色
                    char_shading = run.font._element.rPr.find(qn("w:shd"))
                    if char_shading is not None:
                        fill_attr = char_shading.get(qn("w:fill"))
                        if fill_attr:
                            if re.match(r'^[0-9A-Fa-f]{6}$', fill_attr):
                                hex_color = f"#{fill_attr}"
                                if self._is_color_similar(hex_color, target_bg_color_hex):
                                    logger.info(f"发现字符底纹背景色: '{run.text}'，颜色值: {hex_color}")
                                    bg_exist = True
                                    bg_texts.append(run.text)

        # 过滤掉空字符串
        fg_texts = [text for text in fg_texts if text.strip()]
        bg_texts = [text for text in bg_texts if text.strip()]
        
        # 将匹配文本内容合并为字符串
        fg_texts_str = '\n\n'.join(fg_texts)
        bg_texts_str = '\n\n'.join(bg_texts)
        
        return (fg_exist, fg_texts_str), (bg_exist, bg_texts_str)
    def _is_color_similar(self, color1: str, color2: str, tolerance: int = 30) -> bool:
        """
        判断两个十六进制颜色值是否足够接近
        
        Args:
            color1: 第一个颜色值 (#RRGGBB)
            color2: 第二个颜色值 (#RRGGBB)
            tolerance: 容差阈值 (0-255)
        
        Returns:
            bool: 颜色差异是否小于等于指定容差
        """
        # 移除可能的#符号并转换为整数
        r1, g1, b1 = int(color1[1:3], 16), int(color1[3:5], 16), int(color1[5:7], 16)
        r2, g2, b2 = int(color2[1:3], 16), int(color2[3:5], 16), int(color2[5:7], 16)
        
        # 计算欧氏距离
        distance = math.sqrt((r1 - r2)**2 + (g1 - g2)**2 + (b1 - b2)**2)
        
        return distance <= tolerance         

    def _highlight_color_to_rgb(self, highlight_color):
        """
        将Word高亮颜色枚举转换为RGB值
        
        参数:
        - highlight_color: WD_COLOR_INDEX枚举值
        
        返回:
        - tuple: (R, G, B) 颜色值，如果无法映射则返回None
        """
        # 高亮颜色到RGB的映射表
        color_map = {
            WD_COLOR_INDEX.YELLOW: (255, 255, 0),      # 黄色
            WD_COLOR_INDEX.BRIGHT_GREEN: (0, 255, 0),  # 亮绿色
            WD_COLOR_INDEX.TURQUOISE: (0, 255, 255),   # 青绿色
            WD_COLOR_INDEX.PINK: (255, 0, 255),        # 粉色
            WD_COLOR_INDEX.BLUE: (0, 0, 255),          # 蓝色
            WD_COLOR_INDEX.RED: (255, 0, 0),           # 红色
            WD_COLOR_INDEX.DARK_BLUE: (0, 0, 128),     # 深蓝色
            WD_COLOR_INDEX.TEAL: (0, 128, 128),        # 蓝绿色
            WD_COLOR_INDEX.GREEN: (0, 128, 0),         # 绿色
            WD_COLOR_INDEX.VIOLET: (128, 0, 128),      # 紫色
            WD_COLOR_INDEX.DARK_RED: (128, 0, 0),      # 深红色
            WD_COLOR_INDEX.DARK_YELLOW: (128, 128, 0), # 深黄色
            WD_COLOR_INDEX.GRAY_50: (128, 128, 128),   # 50%灰色
            WD_COLOR_INDEX.GRAY_25: (192, 192, 192),   # 25%灰色
            WD_COLOR_INDEX.BLACK: (0, 0, 0),           # 黑色
            WD_COLOR_INDEX.WHITE: (255, 255, 255),     # 白色
        }
        
        return color_map.get(highlight_color, None)

    def _show_progress_bar(self,times):
        steps = [("提示词生成",1), ("模型调用",times)]
        self.progress_bar = ProgressOverlay(self.root, steps)  # 保存引用
        self.progress_bar.show()
        return self.progress_bar  # 返回进度条实例
        
    def _run_model_call_in_thread(self, model_name, model_api_key, model_base_url, model_max_tokens, model_temperature, model_stream, prompts):
        """在子线程中执行模型调用"""
        try:
            result = LLMProcess.getLLMTextResponse(model_name, model_api_key, model_base_url, model_max_tokens,model_temperature, model_stream, prompts)
            result = JsonUtil.reshape_markdown(result)
            logger.info(f"检查结果：{result}")
            
            # 回到主线程隐藏进度条并弹窗
            self.root.after(0, lambda: [
                self.progress_bar.safe_hide(),
                CustomDialog(self.root, "AI检查结果", result, type='info')
            ])
        except Exception as e:
            error_message = f"模型调用过程中发生错误：{str(e)}"
            logger.error(error_message, exc_info=True)
            self.root.after(0, lambda: [
                self.progress_bar.safe_hide(),
                CustomDialog(self.root, "错误", error_message, type='error')
            ])